using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Text;
using ns15;
using ns18;
using ns20;

namespace Newtonsoft.Json.Bson
{
	public class BsonReader : JsonReader
	{
		private enum Enum10
		{
			Normal,
			ReferenceStart,
			ReferenceRef,
			ReferenceId,
			CodeWScopeStart,
			CodeWScopeCode,
			CodeWScopeScope,
			CodeWScopeScopeObject,
			CodeWScopeScopeEnd
		}

		private class Class70
		{
			public readonly Enum11 enum11_0;

			public int int_0;

			public int int_1;

			public Class70(Enum11 type)
			{
				enum11_0 = type;
			}
		}

		private const int int_0 = 128;

		private static readonly byte[] byte_0 = new byte[2] { 0, 127 };

		private static readonly byte[] byte_1 = new byte[2] { 194, 223 };

		private static readonly byte[] byte_2 = new byte[2] { 224, 239 };

		private static readonly byte[] byte_3 = new byte[2] { 240, 244 };

		private readonly BinaryReader binaryReader_0;

		private readonly List<Class70> list_1;

		private byte[] byte_4;

		private char[] char_1;

		private Enum11 enum11_0;

		private Enum10 enum10_0;

		private Class70 class70_0;

		private bool bool_2;

		private bool bool_3;

		private DateTimeKind dateTimeKind_0;

		[Obsolete("JsonNet35BinaryCompatibility will be removed in a future version of Json.NET.")]
		public bool JsonNet35BinaryCompatibility
		{
			get
			{
				return bool_3;
			}
			set
			{
				bool_3 = value;
			}
		}

		public bool ReadRootValueAsArray
		{
			get
			{
				return bool_2;
			}
			set
			{
				bool_2 = value;
			}
		}

		public DateTimeKind DateTimeKindHandling
		{
			get
			{
				return dateTimeKind_0;
			}
			set
			{
				dateTimeKind_0 = value;
			}
		}

		public BsonReader(Stream stream)
			: this(stream, readRootValueAsArray: false, DateTimeKind.Local)
		{
		}

		public BsonReader(BinaryReader reader)
			: this(reader, readRootValueAsArray: false, DateTimeKind.Local)
		{
		}

		public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
		{
			Class204.smethod_2(stream, "stream");
			binaryReader_0 = new BinaryReader(stream);
			list_1 = new List<Class70>();
			bool_2 = readRootValueAsArray;
			dateTimeKind_0 = dateTimeKindHandling;
		}

		public BsonReader(BinaryReader reader, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling)
		{
			Class204.smethod_2(reader, "reader");
			binaryReader_0 = reader;
			list_1 = new List<Class70>();
			bool_2 = readRootValueAsArray;
			dateTimeKind_0 = dateTimeKindHandling;
		}

		private string method_14()
		{
			enum11_0 = method_32();
			return method_23();
		}

		public override byte[] ReadAsBytes()
		{
			return method_5();
		}

		public override decimal? ReadAsDecimal()
		{
			return method_6();
		}

		public override int? ReadAsInt32()
		{
			return method_7();
		}

		public override string ReadAsString()
		{
			return method_8();
		}

		public override DateTime? ReadAsDateTime()
		{
			return method_9();
		}

		public override DateTimeOffset? ReadAsDateTimeOffset()
		{
			return method_4();
		}

		public override bool Read()
		{
			enum14_0 = Enum14.Read;
			return Newtonsoft_002EJson_002EJsonReader_002E_202E_206F_206A_206A_202D_202B_206A_202E_206F_206F_200B_200B_200F_206D_200F_206F_200C_200E_202A_202E_202A_206C_202B_200C_202A_206E_200C_202C_200D_200E_202D_200C_206D_200F_202B_206A_206B_200D_206C_202D_202E();
		}

		internal override bool Newtonsoft_002EJson_002EJsonReader_002E_202E_206F_206A_206A_202D_202B_206A_202E_206F_206F_200B_200B_200F_206D_200F_206F_200C_200E_202A_202E_202A_206C_202B_200C_202A_206E_200C_202C_200D_200E_202D_200C_206D_200F_202B_206A_206B_200D_206C_202D_202E()
		{
			try
			{
				bool flag;
				switch (enum10_0)
				{
				default:
					throw JsonReaderException.smethod_1(this, "Unexpected state: {0}".smethod_0(CultureInfo.InvariantCulture, enum10_0));
				case Enum10.Normal:
					flag = method_17();
					break;
				case Enum10.ReferenceStart:
				case Enum10.ReferenceRef:
				case Enum10.ReferenceId:
					flag = method_16();
					break;
				case Enum10.CodeWScopeStart:
				case Enum10.CodeWScopeCode:
				case Enum10.CodeWScopeScope:
				case Enum10.CodeWScopeScopeObject:
				case Enum10.CodeWScopeScopeEnd:
					flag = method_15();
					break;
				}
				if (!flag)
				{
					SetToken(JsonToken.None);
					return false;
				}
				return true;
			}
			catch (EndOfStreamException)
			{
				SetToken(JsonToken.None);
				return false;
			}
		}

		public override void Close()
		{
			base.Close();
			if (base.CloseInput && binaryReader_0 != null)
			{
				binaryReader_0.Close();
			}
		}

		private bool method_15()
		{
			switch (enum10_0)
			{
			default:
				throw new ArgumentOutOfRangeException();
			case Enum10.CodeWScopeStart:
				SetToken(JsonToken.PropertyName, "$code");
				enum10_0 = Enum10.CodeWScopeCode;
				return true;
			case Enum10.CodeWScopeCode:
				method_30();
				SetToken(JsonToken.String, method_24());
				enum10_0 = Enum10.CodeWScopeScope;
				return true;
			case Enum10.CodeWScopeScope:
			{
				if (base.CurrentState == State.PostValue)
				{
					SetToken(JsonToken.PropertyName, "$scope");
					return true;
				}
				SetToken(JsonToken.StartObject);
				enum10_0 = Enum10.CodeWScopeScopeObject;
				Class70 @class = new Class70(Enum11.Object);
				method_19(@class);
				@class.int_0 = method_30();
				return true;
			}
			case Enum10.CodeWScopeScopeObject:
			{
				bool result;
				if ((result = method_17()) && JsonToken_0 == JsonToken.EndObject)
				{
					enum10_0 = Enum10.CodeWScopeScopeEnd;
				}
				return result;
			}
			case Enum10.CodeWScopeScopeEnd:
				SetToken(JsonToken.EndObject);
				enum10_0 = Enum10.Normal;
				return true;
			}
		}

		private bool method_16()
		{
			switch (base.CurrentState)
			{
			default:
				throw JsonReaderException.smethod_1(this, "Unexpected state when reading BSON reference: " + base.CurrentState);
			case State.PostValue:
				if (enum10_0 == Enum10.ReferenceRef)
				{
					SetToken(JsonToken.PropertyName, "$id");
					enum10_0 = Enum10.ReferenceId;
					return true;
				}
				if (enum10_0 == Enum10.ReferenceId)
				{
					SetToken(JsonToken.EndObject);
					enum10_0 = Enum10.Normal;
					return true;
				}
				throw JsonReaderException.smethod_1(this, "Unexpected state when reading BSON reference: " + enum10_0);
			case State.Property:
				if (enum10_0 == Enum10.ReferenceRef)
				{
					SetToken(JsonToken.String, method_24());
					return true;
				}
				if (enum10_0 == Enum10.ReferenceId)
				{
					SetToken(JsonToken.Bytes, method_34(12));
					return true;
				}
				throw JsonReaderException.smethod_1(this, "Unexpected state when reading BSON reference: " + enum10_0);
			case State.ObjectStart:
				SetToken(JsonToken.PropertyName, "$ref");
				enum10_0 = Enum10.ReferenceRef;
				return true;
			}
		}

		private bool method_17()
		{
			switch (base.CurrentState)
			{
			case State.Start:
			{
				JsonToken token2 = ((!bool_2) ? JsonToken.StartObject : JsonToken.StartArray);
				Enum11 type = ((!bool_2) ? Enum11.Object : Enum11.Array);
				SetToken(token2);
				Class70 class2 = new Class70(type);
				method_19(class2);
				class2.int_0 = method_30();
				return true;
			}
			case State.Property:
				method_21(enum11_0);
				return true;
			default:
				throw new ArgumentOutOfRangeException();
			case State.Complete:
			case State.Closed:
				return false;
			case State.ObjectStart:
			case State.ArrayStart:
			case State.PostValue:
			{
				Class70 @class = class70_0;
				if (@class == null)
				{
					return false;
				}
				int num = @class.int_0 - 1;
				if (@class.int_1 < num)
				{
					if (@class.enum11_0 == Enum11.Array)
					{
						method_14();
						method_21(enum11_0);
						return true;
					}
					SetToken(JsonToken.PropertyName, method_14());
					return true;
				}
				if (@class.int_1 == num)
				{
					if (method_20() != 0)
					{
						throw JsonReaderException.smethod_1(this, "Unexpected end of object byte value.");
					}
					method_18();
					if (class70_0 != null)
					{
						method_33(@class.int_0);
					}
					JsonToken token = ((@class.enum11_0 == Enum11.Object) ? JsonToken.EndObject : JsonToken.EndArray);
					SetToken(token);
					return true;
				}
				throw JsonReaderException.smethod_1(this, "Read past end of current container context.");
			}
			case State.ConstructorStart:
			case State.Constructor:
			case State.Error:
			case State.Finished:
				return false;
			}
		}

		private void method_18()
		{
			list_1.RemoveAt(list_1.Count - 1);
			if (list_1.Count == 0)
			{
				class70_0 = null;
			}
			else
			{
				class70_0 = list_1[list_1.Count - 1];
			}
		}

		private void method_19(Class70 class70_1)
		{
			list_1.Add(class70_1);
			class70_0 = class70_1;
		}

		private byte method_20()
		{
			method_33(1);
			return binaryReader_0.ReadByte();
		}

		private void method_21(Enum11 enum11_1)
		{
			switch (enum11_1)
			{
			default:
				throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + enum11_1);
			case Enum11.Number:
			{
				double num = method_29();
				if (floatParseHandling_0 == FloatParseHandling.Decimal)
				{
					SetToken(JsonToken.Float, Convert.ToDecimal(num, CultureInfo.InvariantCulture));
				}
				else
				{
					SetToken(JsonToken.Float, num);
				}
				break;
			}
			case Enum11.Object:
			{
				SetToken(JsonToken.StartObject);
				Class70 class2 = new Class70(Enum11.Object);
				method_19(class2);
				class2.int_0 = method_30();
				break;
			}
			case Enum11.Array:
			{
				SetToken(JsonToken.StartArray);
				Class70 @class = new Class70(Enum11.Array);
				method_19(@class);
				@class.int_0 = method_30();
				break;
			}
			case Enum11.Binary:
				SetToken(JsonToken.Bytes, method_22());
				break;
			case Enum11.Undefined:
				SetToken(JsonToken.Undefined);
				break;
			case Enum11.Oid:
			{
				byte[] value2 = method_34(12);
				SetToken(JsonToken.Bytes, value2);
				break;
			}
			case Enum11.Boolean:
			{
				bool flag = Convert.ToBoolean(method_20());
				SetToken(JsonToken.Boolean, flag);
				break;
			}
			case Enum11.Date:
			{
				long long_ = method_31();
				DateTime dateTime = Class185.smethod_11(long_);
				SetToken(JsonToken.Date, DateTimeKindHandling switch
				{
					DateTimeKind.Unspecified => DateTime.SpecifyKind(dateTime, DateTimeKind.Unspecified), 
					DateTimeKind.Local => dateTime.ToLocalTime(), 
					_ => dateTime, 
				});
				break;
			}
			case Enum11.Null:
				SetToken(JsonToken.Null);
				break;
			case Enum11.Regex:
			{
				string text = method_23();
				string text2 = method_23();
				string value = "/" + text + "/" + text2;
				SetToken(JsonToken.String, value);
				break;
			}
			case Enum11.Reference:
				SetToken(JsonToken.StartObject);
				enum10_0 = Enum10.ReferenceStart;
				break;
			case Enum11.Code:
				SetToken(JsonToken.String, method_24());
				break;
			case Enum11.String:
			case Enum11.Symbol:
				SetToken(JsonToken.String, method_24());
				break;
			case Enum11.CodeWScope:
				SetToken(JsonToken.StartObject);
				enum10_0 = Enum10.CodeWScopeStart;
				break;
			case Enum11.Integer:
				SetToken(JsonToken.Integer, (long)method_30());
				break;
			case Enum11.TimeStamp:
			case Enum11.Long:
				SetToken(JsonToken.Integer, method_31());
				break;
			}
		}

		private byte[] method_22()
		{
			int int_ = method_30();
			Enum9 @enum = (Enum9)method_20();
			if (@enum == Enum9.BinaryOld && !bool_3)
			{
				int_ = method_30();
			}
			return method_34(int_);
		}

		private string method_23()
		{
			method_28();
			StringBuilder stringBuilder = null;
			int num = 0;
			int num2 = 0;
			while (true)
			{
				int num3 = num2;
				byte b;
				while (num3 < 128 && (b = binaryReader_0.ReadByte()) > 0)
				{
					byte_4[num3++] = b;
				}
				int num4 = num3 - num2;
				num += num4;
				if (num3 >= 128 || stringBuilder != null)
				{
					int num5 = method_26(num3 - 1);
					int chars = Encoding.UTF8.GetChars(byte_4, 0, num5 + 1, char_1, 0);
					if (stringBuilder == null)
					{
						stringBuilder = new StringBuilder(256);
					}
					stringBuilder.Append(char_1, 0, chars);
					if (num5 < num4 - 1)
					{
						num2 = num4 - num5 - 1;
						Array.Copy(byte_4, num5 + 1, byte_4, 0, num2);
						continue;
					}
					if (num3 < 128)
					{
						break;
					}
					num2 = 0;
					continue;
				}
				int chars2 = Encoding.UTF8.GetChars(byte_4, 0, num4, char_1, 0);
				method_33(num + 1);
				return new string(char_1, 0, chars2);
			}
			method_33(num + 1);
			return stringBuilder.ToString();
		}

		private string method_24()
		{
			int num = method_30();
			method_33(num);
			string result = method_25(num - 1);
			binaryReader_0.ReadByte();
			return result;
		}

		private string method_25(int int_1)
		{
			if (int_1 == 0)
			{
				return string.Empty;
			}
			method_28();
			StringBuilder stringBuilder = null;
			int num = 0;
			int num2 = 0;
			do
			{
				int count = ((int_1 - num <= 128 - num2) ? (int_1 - num) : (128 - num2));
				int num3 = binaryReader_0.Read(byte_4, num2, count);
				if (num3 != 0)
				{
					num += num3;
					num3 += num2;
					if (num3 != int_1)
					{
						int num4 = method_26(num3 - 1);
						if (stringBuilder == null)
						{
							stringBuilder = new StringBuilder(int_1);
						}
						int chars = Encoding.UTF8.GetChars(byte_4, 0, num4 + 1, char_1, 0);
						stringBuilder.Append(char_1, 0, chars);
						if (num4 < num3 - 1)
						{
							num2 = num3 - num4 - 1;
							Array.Copy(byte_4, num4 + 1, byte_4, 0, num2);
						}
						else
						{
							num2 = 0;
						}
						continue;
					}
					int chars2 = Encoding.UTF8.GetChars(byte_4, 0, num3, char_1, 0);
					return new string(char_1, 0, chars2);
				}
				throw new EndOfStreamException("Unable to read beyond the end of the stream.");
			}
			while (num < int_1);
			return stringBuilder.ToString();
		}

		private int method_26(int int_1)
		{
			int num = int_1;
			int num2 = 0;
			for (; num >= 0; num--)
			{
				num2 = method_27(byte_4[num]);
				switch (num2)
				{
				case 0:
					continue;
				default:
					num--;
					break;
				case 1:
					break;
				}
				break;
			}
			if (num2 == int_1 - num)
			{
				return int_1;
			}
			return num;
		}

		private int method_27(byte byte_5)
		{
			if (byte_5 <= byte_0[1])
			{
				return 1;
			}
			if (byte_5 >= byte_1[0] && byte_5 <= byte_1[1])
			{
				return 2;
			}
			if (byte_5 >= byte_2[0] && byte_5 <= byte_2[1])
			{
				return 3;
			}
			if (byte_5 >= byte_3[0] && byte_5 <= byte_3[1])
			{
				return 4;
			}
			return 0;
		}

		private void method_28()
		{
			if (byte_4 == null)
			{
				byte_4 = new byte[128];
			}
			if (char_1 == null)
			{
				int maxCharCount = Encoding.UTF8.GetMaxCharCount(128);
				char_1 = new char[maxCharCount];
			}
		}

		private double method_29()
		{
			method_33(8);
			return binaryReader_0.ReadDouble();
		}

		private int method_30()
		{
			method_33(4);
			return binaryReader_0.ReadInt32();
		}

		private long method_31()
		{
			method_33(8);
			return binaryReader_0.ReadInt64();
		}

		private Enum11 method_32()
		{
			method_33(1);
			return (Enum11)binaryReader_0.ReadSByte();
		}

		private void method_33(int int_1)
		{
			class70_0.int_1 += int_1;
		}

		private byte[] method_34(int int_1)
		{
			method_33(int_1);
			return binaryReader_0.ReadBytes(int_1);
		}
	}
}
